package com.envy.securityjpa.config;

import com.envy.securityjpa.entity.User;
import com.envy.securityjpa.filter.EnvyLoginFilter;
import com.envy.securityjpa.filter.VerifyCodeFilter;
import com.envy.securityjpa.service.MyUserDetailService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private VerifyCodeFilter verifyCodeFilter;

    @Autowired
    private MyUserDetailService myUserDetailService;

    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailService);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**", "/css/**","/vercode","/VerifyCode.html");
    }

    /**
     * 前后端分离模式下，登录使用Key-Value键值对传递参数
     * */
//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
//        http.authorizeRequests()
//                .antMatchers("/admin/**").hasRole("admin")
//                .antMatchers("/user/**").hasRole("user")
//                .anyRequest().authenticated()
//                .and()
//                .formLogin()
//                .loginPage("/login.html")
//                .loginProcessingUrl("/goLogin")
//                .usernameParameter("name")
//                .passwordParameter("word")
//                .successHandler((httpServletRequest, httpServletResponse, authentication) ->{
//                    Object principal = authentication.getPrincipal();
//                    httpServletResponse.setContentType("application/json;charset=utf-8");
//                    PrintWriter out = httpServletResponse.getWriter();
//                    out.write(new ObjectMapper().writeValueAsString(principal));
//                    out.flush();
//                    out.close(); }
//                )
//                .failureHandler((httpServletRequest, httpServletResponse, e)-> {
//                    httpServletResponse.setContentType("application/json;charset=utf-8");
//                    PrintWriter out = httpServletResponse.getWriter();
//                    httpServletResponse.setStatus(401);
//                    Map<String, Object> map = new HashMap<>();
//                    map.put("status", 401);
//                    if (e instanceof LockedException) {
//                        map.put("msg", "账户被锁定，登录失败！");
//                    } else if (e instanceof BadCredentialsException) {
//                        map.put("msg", "账户名或密码输入错误，登录失败！");
//                    } else if (e instanceof DisabledException) {
//                        map.put("msg", "账户被禁用，登录失败！");
//                    } else if (e instanceof AccountExpiredException) {
//                        map.put("msg", "账户已过期，登录失败！");
//                    } else {
//                        map.put("msg", "登录失败！");
//                    }
//                    ObjectMapper objectMapper = new ObjectMapper();
//                    out.write(objectMapper.writeValueAsString(map));
//                    out.flush();
//                    out.close();
//                })
//                .permitAll()
//                .and()
//                .csrf().disable()
//                .exceptionHandling()
//                .authenticationEntryPoint((httpServletRequest,httpServletResponse,authException)->{
//                    httpServletResponse.setContentType("application/json;charset=utf-8");
//                    PrintWriter out = httpServletResponse.getWriter();
//                    out.write("您尚未登录，请先登录!");
//                    out.flush();
//                    out.close();
//                })
//                .and()
//                .logout()
//                .logoutUrl("/logout")
//                .logoutSuccessHandler((httpServletRequest,httpServletResponse,authentication)->{
//                    httpServletResponse.setContentType("application/json;charset=utf-8");
//                    PrintWriter out = httpServletResponse.getWriter();
//                    out.write("注销登录成功!");
//                    out.flush();
//                    out.close();
//         }).and();
//    }


    //角色继承
    @Bean
    RoleHierarchy roleHierarchy(){
        RoleHierarchyImpl hierarchy= new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_admin > ROLE_user");
        return hierarchy;
    }


    /**
     * 前后端分离模式下，登录使用JSON传递参数
     * */
    //自定义过滤器
    @Bean
    EnvyLoginFilter envyLoginFilter() throws Exception {
        EnvyLoginFilter envyLoginFilter = new EnvyLoginFilter();
        //设置登录成功时逻辑
        envyLoginFilter.setAuthenticationSuccessHandler((httpServletRequest,httpServletResponse,authentication)->{
            httpServletResponse.setContentType("application/json;charset=utf-8");
            PrintWriter out = httpServletResponse.getWriter();
            User user = (User) authentication.getPrincipal();
            user.setPassword(null);

            Map<String,Object> map= new HashMap();

            map.put("status",200);
            map.put("user",user);
            out.write(new ObjectMapper().writeValueAsString(map));
            out.flush();
            out.close();
        });
        //设置登录失败时逻辑
        envyLoginFilter.setAuthenticationFailureHandler((httpServletRequest,httpServletResponse,exception)->{
            httpServletResponse.setContentType("application/json;charset=utf-8");
            PrintWriter out = httpServletResponse.getWriter();

            Map<String,Object> map= new HashMap();
            map.put("status",401);
            if (exception instanceof LockedException) {
                map.put("msg", "账户被锁定，请联系管理员!");
            } else if (exception instanceof BadCredentialsException) {
                map.put("msg", "账户名或密码输入错误，请重新输入！");
            } else if (exception instanceof DisabledException) {
                map.put("msg", "账户被禁用，请联系管理员!");
            } else if (exception instanceof AccountExpiredException) {
                map.put("msg", "账户已过期，请联系管理员!");
            } else if (exception instanceof CredentialsExpiredException) {
                map.put("msg", "密码已过期，请联系管理员!");
            } else {
                map.put("msg", "登录失败！");
            }
            out.write(new ObjectMapper().writeValueAsString(map));
            out.flush();
            out.close();
        });
        envyLoginFilter.setAuthenticationManager(authenticationManagerBean());
        envyLoginFilter.setFilterProcessesUrl("/goLogin");
        return envyLoginFilter;
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasRole("user")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/goLogin").permitAll()
                .and()
                .rememberMe()
                .and()
                .csrf().disable();
        http.addFilterAt(envyLoginFilter(),UsernamePasswordAuthenticationFilter.class);
    }

}
